/**
该程序为分析TS文件，并获取音频时长和视频时长以及总时长；
处理逻辑：
1、输入或者远程的TS文件
2、读取TS文件中的分片信息,根据pts计算该视频的时长信息；
3、以json方式输出该信息；
*/

#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>
#include <sys/types.h> 
#include <sys/stat.h>
#include <unistd.h>

#define TS_FILE_DAMAGE 1        //文件损坏
#define TS_FILE_VIDEO_DAMAGE 2  //视频文件损坏
#define TS_FILE_AUDIO_DAMAGE 3  //音频文件损坏
#define VIDEO_STREAM    1       //视频流
#define AUDIO_STREAM    2       //音频流

FILE *f;

struct stminfo
{
    int64_t min_dts;        //最小的dts
    int64_t max_dts;        //最大的dts
    int64_t min_pts;        //最小的pts
    int64_t max_pts;        //最大的pts
    int64_t duration;       //每帧的时间长度
    int stype;              //流类型，0:无 VIDEO_STREAM:视频 AUDIO_STREAM:音频
    int snum;               //该流的最大帧数
};

//如果为0 ，则没有B帧
int B_FRAME = 0;
int crt_stream = 0;
int isFirst = 1;
int adj_i = 0;
//定义输出流的全局的变量值
struct stminfo out_stifo[10];

int main(int argc, char **argv)
{
    char *in_filename, *path;
    char work_abs_path[512];
    struct timeval tv, tv1;
    gettimeofday(&tv, NULL);

    if (argc < 2) {
        fprintf(stdout, "Usage: gtd {filename}.ts \n\n");
        return 1;
    }
    long timestamp = tv.tv_sec * 1000*1000 + tv.tv_usec;    
    fprintf(stdout, "input: %s \n Work wating......\n", argv[1]);
    
    in_filename  = argv[1];

    analyzie_ts(in_filename);

    gettimeofday(&tv1, NULL);
    timestamp = (tv1.tv_sec * 1000*1000 + tv1.tv_usec - timestamp)/1000;
    fprintf(stdout, "\n处理完成, 共用时: %ld 毫秒 \n", timestamp);
}

void analyzie_ts(char *in_filename){

    //定义输入和输出的上下文结构数组
    AVFormatContext *ifmt_ctx = NULL;
    AVStream *in_stream;
    AVPacket pkt;
    int ret, i;
    int adj_flag = 0;
    int stream_index = 0;
    int *stream_mapping = NULL;
    int stream_mapping_size = 0;


    //找开输入文件
    if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) {
        fprintf(stderr, "Could not open input file '%s\n'", in_filename);
        return;
    }
    //识别输入文件的流信息
    if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {
        fprintf(stderr, "Failed to retrieve input stream information");
        return;
    }

    stream_mapping_size = ifmt_ctx->nb_streams;
    stream_mapping = av_mallocz_array(stream_mapping_size, sizeof(*stream_mapping));
    if (!stream_mapping) {
        ret = AVERROR(ENOMEM);
        return;
    }

    int stnum = 0;  //流数量计数;

    //循环读取文件中的多个流的信息
    for (i = 0; i < ifmt_ctx->nb_streams; i++) {
        in_stream = ifmt_ctx->streams[i];
        AVCodecParameters *in_codecpar = in_stream->codecpar;

        if (in_codecpar->codec_type != AVMEDIA_TYPE_AUDIO &&
            in_codecpar->codec_type != AVMEDIA_TYPE_VIDEO &&
            in_codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) {
            stream_mapping[i] = -1;
            continue;
        }

        stream_mapping[i] = stream_index++;

        if (in_stream->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
            out_stifo[i].stype = AUDIO_STREAM;
            stnum +=1;
        }
                
        if (in_stream->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
            out_stifo[i].stype = VIDEO_STREAM;
            stnum +=2;
        }
    }
    //如果stnum>2则认为其存在视音频的内容，否则就不进行处理
    if( stnum <3) goto end;

    //读取流信息，生成所有pkt的结构指针，并对于结构指针数据进行分析;
    for(;;) {

        ret = av_read_frame(ifmt_ctx, &pkt);
        if (ret < 0)
            break;
        // fprintf(stdout, "file: %s, stream_id: %d, source_pts:%ld, source_dts:%ld, source_duration: %ld \n", in_filename, pkt.stream_index, pkt.pts, pkt.dts, pkt.duration);
        //有时会出现编码器数值越界出现负数的情况
        if(pkt.pts < 0)
            continue;

        if(pkt.pts != pkt.dts && pkt.dts != 0) {
            B_FRAME = 1;
        }

        in_stream  = ifmt_ctx->streams[pkt.stream_index];
        if (pkt.stream_index >= stream_mapping_size ||
            stream_mapping[pkt.stream_index] < 0) {
            av_packet_unref(&pkt);
            continue;
        }

        //把流的信息写入到stifo中；
        if (out_stifo[pkt.stream_index].duration == 0 && out_stifo[pkt.stream_index].min_dts == 0) {

            out_stifo[pkt.stream_index].duration = pkt.duration;
            out_stifo[pkt.stream_index].min_pts = pkt.pts;
            out_stifo[pkt.stream_index].min_dts = pkt.dts; 
            out_stifo[pkt.stream_index].max_pts = pkt.pts - pkt.duration;
            out_stifo[pkt.stream_index].max_dts = pkt.dts;
        }
        //还有一种情况是信息中没有带duration的值;这种情况下只能是通过当前帧减后一帧的值来计算一个大致的值;
        if (( out_stifo[pkt.stream_index].duration == 0) && (out_stifo[pkt.stream_index].min_pts != pkt.pts)) {
            out_stifo[pkt.stream_index].duration = pkt.pts - out_stifo[pkt.stream_index].min_pts;
        }
        if (pkt.duration == 0 && out_stifo[pkt.stream_index].duration != 0) 
            pkt.duration = out_stifo[pkt.stream_index].duration;

        if (pkt.duration == 0 && out_stifo[pkt.stream_index].duration == 0 && out_stifo[pkt.stream_index].stype == VIDEO_STREAM) 
            pkt.duration = 3600;

        //有一种情况是当前的pkt的pts与max_pts相等，说明其为重复帧，则不进行处理，直接读下一帧;
        if(pkt.pts == out_stifo[pkt.stream_index].max_pts && out_stifo[pkt.stream_index].duration != 0) {
            av_packet_unref(&pkt);
            continue;
        }
        
        out_stifo[pkt.stream_index].min_pts = pkt.pts>out_stifo[pkt.stream_index].min_pts?out_stifo[pkt.stream_index].min_pts:pkt.pts;
        out_stifo[pkt.stream_index].min_dts = pkt.dts>out_stifo[pkt.stream_index].min_dts?out_stifo[pkt.stream_index].min_dts:pkt.dts;
        out_stifo[pkt.stream_index].max_pts = pkt.pts<out_stifo[pkt.stream_index].max_pts?out_stifo[pkt.stream_index].max_pts:pkt.pts;
        out_stifo[pkt.stream_index].max_dts = pkt.dts<out_stifo[pkt.stream_index].max_dts?out_stifo[pkt.stream_index].max_dts:pkt.dts;
        av_packet_unref(&pkt);
    }
end:    
    crt_stream = 1;
    avformat_close_input(&ifmt_ctx);
    av_freep(&stream_mapping);

    int min_p = 0, max_p = 0;
    //根据out_stifo结果来输出视音频的时长信息
    for (int i = 0; i <10; i++) {
        float a_t = 0;
        char tname[10] = {0};

        switch(out_stifo[i].stype) { 
            case 1:
                strcpy(tname, "video");
                break;
            case 2:
                strcpy(tname, "audio");
                break;
            default:
                strcpy(tname, "none");
                continue;
        };
        out_stifo[i].max_pts = out_stifo[i].max_pts + out_stifo[i].duration;

        if (min_p == 0)
            min_p = out_stifo[i].min_pts;
        if (max_p == 0)
            max_p = out_stifo[i].max_pts;

        min_p = min_p > out_stifo[i].min_pts?out_stifo[i].min_pts:min_p;
        max_p = max_p < out_stifo[i].max_pts?out_stifo[i].max_pts:max_p;

        a_t = (float)(out_stifo[i].max_pts - out_stifo[i].min_pts) / 90000.00;
        printf("type: %s, min_pts: %ld, max_pts: %ld, duration: %.2f\n", tname, out_stifo[i].min_pts, out_stifo[i].max_pts, a_t);
    }
    float a_t = 0;
    a_t = (float)(max_p - min_p) / 90000.00;
    printf("type: total, min_pts: %ld, max_pts: %ld, duration: %.2f\n",  min_p, max_p, a_t);
    return;
}